using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using FarseerPhysics.Collision;
using FarseerPhysics.Common;
using System.Text;

namespace UtilityLibrary
{
    public class PrimitiveBrush
    {
        private static Texture2D point;
        private static Texture2D circleText;
        private static SpriteFont font;

        private static Vector2 scale = new Vector2();
        private static Vector2 position = new Vector2();
        private static Vector2[] points = new Vector2[4];

        /// <summary>
        /// Load data for primitive rendering
        /// </summary>
        /// <param name="content"></param>
        public static void LoadContent(ContentManager content)
        {
            point = content.Load<Texture2D>(@"Point");
            circleText = content.Load<Texture2D>(@"Circle");
            font = content.Load<SpriteFont>(@"Arial");
        }

        /// <summary>
        /// Renders a red line between two points.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="point1"></param>
        /// <param name="point2"></param>
        public static void RenderLine(SpriteBatch batch, Vector2 point1, Vector2 point2)
        {
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
            float length = Vector2.Distance(point1, point2);

            position.X = point1.X;
            position.Y = point1.Y;
            scale.X = length;
            scale.Y = 1.0f;


            batch.Draw(point,
                position,
                null,
                Color.Red,
                angle,
                Vector2.Zero,
                scale,
                SpriteEffects.None,
                0.0f);
        }

        /// <summary>
        /// Renders a line of the specified color between two points.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="point1"></param>
        /// <param name="point2"></param>
        /// <param name="col"></param>
        public static void RenderLine(SpriteBatch batch, Vector2 point1, Vector2 point2, Color col)
        {
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
            float length = Vector2.Distance(point1, point2);

            position.X = point1.X;
            position.Y = point1.Y;
            scale.X = length;
            scale.Y = 1.0f;


            batch.Draw(point,
                position,
                null,
                col,
                angle,
                Vector2.Zero,
                scale,
                SpriteEffects.None,
                0.0f);
        }

        /// <summary>
        /// Renders the provided Rectangle in the color Red.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="rect"></param>
        public static void RenderRect(SpriteBatch batch, Rectangle rect)
        {
            CalculatePoints(rect);

            for (int i = 0; i < points.Length - 1; i++)
            {
                RenderLine(batch, points[i], points[(i + 1)]);
            }

            RenderLine(batch, points[0], points[3]);
        }

        /// <summary>
        /// Renders the specified Rectangle in the specified Color
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="rect"></param>
        /// <param name="col"></param>
        public static void RenderRect(SpriteBatch batch, Rectangle rect, Color col)
        {
            CalculatePoints(rect);

            for (int i = 0; i < points.Length - 1; i++)
            {
                RenderLine(batch, points[i], points[(i + 1)], col);
            }

            RenderLine(batch, points[0], points[3], col);
        }

        /// <summary>
        /// Renders the specified AABB in red.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="rect"></param>
        public static void RenderRect(SpriteBatch batch, AABB rect)
        {
            RenderRect(batch, rect, Color.Red);
        }

        /// <summary>
        /// Renders the specified AABB in the specified color.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="rect"></param>
        /// <param name="col"></param>
        public static void RenderRect(SpriteBatch batch, AABB rect, Color col)
        {
            Vector2[] points = rect.GetVertices();

            for (int i = 0; i < points.Length - 1; i++)
            {
                RenderLine(batch, points[i], points[(i + 1)], col);
            }

            RenderLine(batch, points[0], points[3], col);
        }

        /// <summary>
        /// Renders the convex hull in red.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="hull"></param>
        public static void RenderHull(SpriteBatch batch, List<Vector2> hull)
        {
            for (int i = 0; i < hull.Count; i++)
            {
                RenderLine(batch,
                    hull.ElementAt(i),
                    hull.ElementAt((i + 1) % hull.Count));
            }
        }

        /// <summary>
        /// Renders the convex hull in the specified color.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="hull"></param>
        /// <param name="col"></param>
        public static void RenderHull(SpriteBatch batch, List<Vector2> hull, Color col)
        {
            for (int i = 0; i < hull.Count; i++)
            {
                RenderLine(batch,
                    hull.ElementAt(i),
                    hull.ElementAt((i + 1) % hull.Count),
                    col);
            }
        }

        /// <summary>
        /// Renders a 50 radius point at the specified location in red.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="pos"></param>
        public static void RenderPoint(SpriteBatch batch, Vector2 pos)
        {
            RenderPoint(batch, pos, Color.Red);
        }

        /// <summary>
        /// Renders a 50 radius point at the specified location in the specified color.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="pos"></param>
        /// <param name="col"></param>
        public static void RenderPoint(SpriteBatch batch, Vector2 pos, Color col)
        {
            batch.Draw(circleText, pos,
                null,
                col,
                0.0f,
                new Vector2(circleText.Bounds.Center.X, circleText.Bounds.Center.Y),
                    1.0f,
                    SpriteEffects.None,
                    0.0f);
        }

        /// <summary>
        /// Renders the specified text at the specified position.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="toRender"></param>
        /// <param name="pos"></param>
        public static void RenderText(SpriteBatch batch, string toRender, Vector2 pos)
        {
            batch.DrawString(font, toRender, pos, Color.White);
        }

        public static void RenderText(SpriteBatch batch, string toRender, Vector2 pos, Color color)
        {
            batch.DrawString(font, toRender, pos, color);
        }

        /// <summary>
        /// Renders the specified text at the specified position.  It wraps the lines so that no
        /// line will exceed lineWidth.
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="toRender"></param>
        /// <param name="pos"></param>
        /// <param name="lineWidth"></param>
        public static void RenderTextWrap(SpriteBatch batch, string toRender, Vector2 pos, float lineWidth)
        {
            RenderTextWrap(batch, toRender, pos, lineWidth, Color.White);
        }

        public static void RenderTextWrap(SpriteBatch batch, string toRender, Vector2 pos, float lineWidth, Color color)
        {
            string toDisp = Wrap(toRender, lineWidth);

            batch.DrawString(font, toDisp, pos, color);
        }

        private static string Wrap(string text, float lineWidth)
        {
            string[] words = text.Split(' ');

            StringBuilder builder = new StringBuilder();

            float lineWidthSoFar = 0f;

            float spaceWidth = font.MeasureString(" ").X;

            foreach (string word in words)
            {
                Vector2 size = font.MeasureString(word);

                if (lineWidthSoFar + size.X < lineWidth)
                {
                    builder.Append(word + " ");
                    lineWidthSoFar += size.X + spaceWidth;
                }
                else
                {
                    builder.Append("\n" + word + " ");
                    lineWidthSoFar = size.X + spaceWidth;
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// Calculate the render points for a rectangle.
        /// </summary>
        /// <param name="rect"></param>
        private static void CalculatePoints(Rectangle rect)
        {
            points[0].X = rect.X;
            points[0].Y = rect.Y;

            points[1].X = rect.X + rect.Width;
            points[1].Y = rect.Y;

            points[2].X = rect.X + rect.Width;
            points[2].Y = rect.Y + rect.Height;

            points[3].X = rect.X;
            points[3].Y = rect.Y + rect.Height;
        }
    }
}
